package com.xyq.job.handler;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xxl.job.core.handler.annotation.XxlJob;
import com.xyq.common.core.constants.CacheConstants;
import com.xyq.common.core.constants.Constants;
import com.xyq.common.redis.service.RedisService;
import com.xyq.job.domain.exam.Exam;
import com.xyq.job.domain.message.Message;
import com.xyq.job.domain.message.MessageText;
import com.xyq.job.domain.message.MessageTextVO;
import com.xyq.job.domain.user.UserScore;
import com.xyq.job.mapper.ExamMapper;
import com.xyq.job.mapper.UserExamMapper;
import com.xyq.job.mapper.UserSubmitMapper;
import com.xyq.job.service.MessageService;
import com.xyq.job.service.MessageTextService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Component
public class ExamXxlJob {
    @Autowired
    private RedisService redisService;

    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private UserSubmitMapper userSubmitMapper ;

    @Autowired
    private MessageService messageService ;

    @Autowired
    private MessageTextService messageTextService ;

    @Autowired
    private UserExamMapper userExamMapper ;

    @XxlJob("examListUpdateHandler")
    public void examListUpdateHandler() {
        log.info("*******执行刷新竞赛自动任务***************");

        // 任务 实现
        // 哪些竞赛要放到 未结束列表里 , 那些竞赛要放到 历史竞赛列表里

        // 和ExamCacheManager里的刷新缓存方法 ,referCache方法 类似

        // 1. 查出 未完成的 竞赛列表
        List<Exam> unFinishList = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .select(Exam::getExamId, Exam::getTitle, Exam::getStartTime, Exam::getEndTime)
                .gt(Exam::getEndTime, LocalDateTime.now())
                .eq(Exam::getStatus, Constants.TRUE)
                .orderByDesc(Exam::getCreateTime));
        // 2. 然后调用 刷新缓存方法  ,刷新redis 里的未完成竞赛列表
        //  传一个EXAM_UNFINISHED_LIST 告诉他 要刷新的是未结束竞赛的列表
        refreshCache(unFinishList, CacheConstants.EXAM_UNFINISHED_LIST);

        // 3. 查出 已结束的历史竞赛列表

        List<Exam> historyList = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .select(Exam::getExamId, Exam::getTitle, Exam::getStartTime, Exam::getEndTime)
                .le(Exam::getEndTime, LocalDateTime.now())
                .eq(Exam::getStatus, Constants.TRUE)
                .orderByDesc(Exam::getCreateTime));
         // 4. 调用刷新缓存方法 , 刷新 redis里的历史竞赛列表
        // 传一个CacheConstants.EXAM_HISTORY_LIST , 告诉他刷新 已结束的竞赛列表
          refreshCache(historyList , CacheConstants.EXAM_HISTORY_LIST);
    }


    // 刷新竞赛结果信息任务
    @XxlJob("examResultHandler")
    public void examResultHandler(){
        log.info("*******执行刷新竞赛结束消息自动任务***************");

        // 获取已经发布的竞赛列表
        // 判断竞赛开始结束时间 : 刷新的是 前一天结束的竞赛
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime prev = now.minusDays(1);

        List<Exam> examList = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .eq(Exam::getStatus,Constants.TRUE)
                .ge(Exam::getEndTime,prev)
                .le(Exam::getEndTime,now));
        // 如果昨天没有结束的竞赛,就返回
        if(CollectionUtil.isEmpty(examList)){
            return;
        }
        // 获取竞赛id列表
        Set<Long> examIdList = examList.stream().map(Exam::getExamId).collect(Collectors.toSet());
        // 统计所有竞赛的分数排行
       List<UserScore> userScoreList = userSubmitMapper.selectUserScoreList(examIdList);
       // 通过竞赛 对userScoreList 进行分组 , 分为一个个竞赛的排行:
        // 现在通过竞赛id , 获取不同竞赛的成绩排行
        Map<Long, List<UserScore>> userScoreMap = userScoreList.stream().collect(Collectors.groupingBy(UserScore::getExamId));

        // 生成消息 , 消息存储到缓存里 和数据库里
        createMessage(examList , userScoreMap);

    }

    // 生成消息
    private void createMessage(List<Exam> examIdList, Map<Long, List<UserScore>> userScoreMap) {
        List<MessageText> messageTextList = new ArrayList<>() ;
        List<Message> messageList = new ArrayList<>() ;

        for(Exam exam : examIdList){
             Long examId = exam.getExamId();
             List<UserScore> userScores = userScoreMap.get(examId);

             // 竞赛参加人总数
            if(userScores == null){
                continue;
            }
              int totalUser = userScores.size() ;
              // 计算排名
             int rank = 1 ;
             // 生成消息
             // 消息包括: 标题 , 内容 , 排名
             for(UserScore userScore : userScores){
                 MessageText messageText = new MessageText() ;
                 // 消息标题
                  String messageTitle = exam.getTitle() + "--已结束";
                 // 构造消息内容
                 String messageContent = "您参加的竞赛: <<" + exam.getTitle() +
                         ">> , 一共有"+totalUser +"人参加 , 您的排名是: " +rank +" 名" ;
                 messageText.setContent(messageContent);
                 messageText.setTitle(messageTitle);

                 // 创建人是系统
                 messageText.setCreateBy(Constants.SYSTEM_USER_ID);
                 messageTextList.add(messageText);

                 // 记录用户成绩排名
                 userScore.setExamRank(rank);

                 // 构造消息
                 Message message = new Message() ;
                 message.setSendId(Constants.SYSTEM_USER_ID);
                 message.setCreateBy(Constants.SYSTEM_USER_ID);
                 message.setReceiveId(userScore.getUserId());
                 messageList.add(message) ;
                 rank++ ;
             }
            // 更新 竞赛排名数据
            userExamMapper.updateUserScoreAndRank(userScores);
            // 同时存储到 redis缓存里
            redisService.rightPushAll(getExamRankListKey(examId),userScores);

         }
        // 存储 消息内容 到数据库 使用批量插入
        messageTextService.batchInsert(messageTextList) ;



        // 创建一个map, 用来批量存储 消息内容详情 的缓存
        Map<String , MessageText> messageTextVOMap = new HashMap<>() ;
            // 消息内容详情在缓存里只需要 标题和内容 +id
        // 存储消息
        for (int i = 0; i < messageTextList.size(); i++) {

            MessageText messageText = messageTextList.get(i);

            String userMessageDetailKey = getUserMessageDetailKey(messageText.getMessageTextId());
            messageTextVOMap.put(userMessageDetailKey , messageText);

            Message message = messageList.get(i);
message.setMessageTextId(messageText.getMessageTextId());
        }
        // 存储消息
        messageService.batchInsert(messageList);

         //批量存储消息详情缓存
        redisService.multiSet(messageTextVOMap);

        // 存储到redis缓存里
        // 根据 接收人不同分开
        Map<Long, List<Message>> userMessageMap = messageList.stream().collect(Collectors.groupingBy(Message::getReceiveId));
        Iterator<Map.Entry<Long, List<Message>>> iterator = userMessageMap.entrySet().iterator();
        while (iterator.hasNext()){
            Map.Entry<Long, List<Message>> next = iterator.next();
            Long receiveId = next.getKey();
            String userMessageListKey = getUserMessageListKey(receiveId);
            List<Long> messageTexts = next.getValue().stream().map(Message::getMessageTextId).toList();
            // 删除先
            redisService.deleteObject(userMessageListKey) ;
            redisService.rightPushAll(userMessageListKey , messageTexts) ;
        }

    }


     // 获取用户消息列表key
     private String getUserMessageListKey(Long userId) {
        return CacheConstants.USER_MESSAGE_LIST +userId ;
    }
    // 获取消息详情key
    private String getUserMessageDetailKey(Long messageTextId) {
        return CacheConstants.USER_MESSAGE_DETAIL +messageTextId ;
    }

    //刷新缓存逻辑
    public void refreshCache(List<Exam> examList ,String examListKey) {
        if (CollectionUtil.isEmpty(examList)) {
            return;
        }
        Map<String, Exam> examMap = new HashMap<>();
        List<Long> examIdList = new ArrayList<>();
        for (Exam exam : examList) {
            examMap.put(getDetailKey(exam.getExamId()), exam);
            examIdList.add(exam.getExamId());
        }
        redisService.multiSet(examMap);  //刷新详情缓存
        redisService.deleteObject(examListKey); // 先删除列表
        redisService.rightPushAll(examListKey, examIdList);      //刷新列表缓存
    }

    // 获取 redis 里存储 竞赛信息的 key
    private String getDetailKey(Long examId) {
        return CacheConstants.EXAM_DETAIL + examId;
    }

    //获取竞赛排名key
    private String getExamRankListKey(Long examId) {
        return CacheConstants.EXAM_RANK_LIST + examId;
    }


}
